/*
Este programa es un analizador sintactico para el compilador del lenguaje de programacion c--
Copyright © 2011  Martinelli, Fernán Gabriel (fmartinelli89@gmail.com)
                  Riberi, Franco Gaspar      (fgriberi@gmail.com)
                  Varea, Agustín	         (agvarea@gmail.com)

Todos los derechos reservados

Este programa es software libre: usted puede redistribuirlo y/o modificarlo conforme a los términos 
de la Licencia Pública General de GNU publicada por la Fundación para el Software Libre, ya sea la 
versión 3 de esta Licencia o (a su elección) cualquier versión posterior.

Este programa se distribuye con el deseo de que le resulte útil, pero SIN GARANTÍAS DE NINGÚN TIPO; 
ni siquiera con las garantías implícitas de COMERCIABILIDAD o APTITUD PARA UN PROPÓSITO DETERMINADO.  
Para más información, consulte la Licencia Pública General de GNU.

Junto con este programa, se debería incluir una copia de la Licencia Pública General de GNU.
De no ser así, ingrese en <http://www.gnu.org/licenses/>.

*/

%{
#include <stdio.h>
#include <stdlib.h> /* Para usar el comando exit que permite cortar la ejecucion */
#include "TadTablaSimbolo.h" /* tabla de simbolos */
#include "Tipo.h" /* para la union */
#include "TipoError.c" /* para almacenar los posibles errores */

extern FILE *yyin; /* Referencia al archivo de entrada */
extern int num_lineas;
extern int num_columnas;
Pila *pila;
Lista *list;

/*variables globales para almacenar el tipo de una variable declarada*/
enum Types tipoRetorno;
enum Types typeSpec;  /*Podria agregarse en la union como un atrib mas para q sea mas eficiente ??? */
boolean contarCiclo; /*0- error break o continue mal ubicado, 1- correcto*/
%}

/* Permite declarar el tipo de token. Define yylval como una union de los tipo (tipoNum) (char*) 
   tipoNum es un * a struct, dado a que necesito definir si se trabaja con enteros o float, por
   lo que defino el struct como un tipo superior (tipoNum) */

%union
{
   int iint;
   float ffloat;		
   char *id;
   struct tipoNum *miTipo;
}

/* Declaracion de los tokens definidos en el archivo anLexico.l. Permite asociar con que tipo esta asociado cada tokens. Valores simbolicos*/
%token  IGUALIGUAL AMPERAMPER BARRABARRA  
%token WHILE BREAK CONTINUE IF ELSE RETURN INT FLOAT 
%token <iint> INTDENOTATION 
%token <ffloat> FLOATDENOTATION
%token <id> IDENTIFIER

/* Define el tipo de dato para simbolos no terminales de nuestra gramatica */
%type <miTipo> Computation Expression Condition Disjunction Conjunction Comparison Factor Term Sum Relation Primary

/* Definicion del simbolo inicial de la gramatica*/
%start Program

/* Indica que no deben haber avisos si hay 1 conflicto shift/reduce y ninguno reduce/reduce (vale para N conflictos) */
%expect 1 

/* Comienza la definición de la gramatica */

/* YYACCEPT corta la ejecucion de yyparse() haciendo que el mismo devuelva 0, para que no siga buscando cadenas siguientes y finalice la ejecución */

%% 	
/* ESTRUCTURA DEL PROGRAMA */
Program : {pila = (Pila *) malloc (sizeof(struct PPila)); initTabla(pila); initListaErrores(); contarCiclo = 0;} 
		 TypeSpecifier {tipoRetorno = typeSpec; } 
		 IDENTIFIER '(' {insertarNivel(pila); list=pila->tope->dato;}
		 declPar ')'
		 Compound {eliminarNivel(pila);} 
        ;																		

declPar : ParameterDecl 
        | ParameterDecl ',' declPar  
	    ;

ParameterDecl : TypeSpecifier IDENTIFIER 	{  
											  if (perteneceLista(pila->tope->dato,$2) == NULL){
												 if (typeSpec == entero){
													 insertar(pila->tope->dato,$2,"entero"); 													
 													 //mostrarTabla(pila);
												 }else{
													 insertar(list,$2,"real"); 
												     //mostrarTabla(pila);

												 }											    												
											  }else{
												 mostrarError(10,num_lineas);														  
												 exit(1);	
 											  }
											}
              ;

Compound : '{' {insertarNivel(pila); } cuerpo '}' {eliminarNivel(pila); }           
	     ;	

cuerpo : var stat
       |
       ;

var : VariableDecl var  
    | /* puede no haber declaracion de variables dentro de un compound */  
    ;

stat : Statement stat 
	 | Statement               
     ;

VariableDecl : TypeSpecifier decl ';' /*{mostrarTabla(pila);}*/
			 ;

decl : decl ',' IDENTIFIER '=' Expression	{ if (perteneceLista(pila->tope->dato,$3) != NULL){
											     mostrarError(3,num_lineas);
												 exit(1);			
											  }else{
										         if (((typeSpec == entero) && ($5->tipo == INT_TIPO)) || ((typeSpec == real) && ($5->tipo == FLOAT_TIPO))){
													/*HACER LA ASIGNACION. Deberia modificar mi tabla de simbolos ???*/
													if (typeSpec == entero){
														insertar(pila->tope->dato, $3, "entero");
													}else{
														insertar(pila->tope->dato, $3, "real");
													}						
												 }else if ((typeSpec == real) && ($5->tipo == INT_TIPO)) {
													 /*HACER LA ASIGNACION */			
													 mostrarError(5,num_lineas);
													 if (typeSpec == entero){
														insertar(pila->tope->dato, $3, "entero");
													 }else{
														insertar(pila->tope->dato, $3, "real");
													 }
												}else{
													mostrarError(1,num_lineas); /*asignacion invalida, decir esperaba un <> en la asignacion*/	
													exit(1);
												}
											  }	
											} 
     | decl ',' IDENTIFIER					{ if (perteneceLista(pila->tope->dato,$3) == NULL){
											    if (typeSpec == entero){
													 insertar(pila->tope->dato,$3,"entero"); 												
													 //mostrarTabla(pila);	
												 }else{
													 insertar(pila->tope->dato,$3,"real"); 
													 //mostrarTabla(pila);
												 }
											  }else{
												 mostrarError(3,num_lineas);
												 exit(1);
											  }	
											} 
     | IDENTIFIER '=' Expression		    { if (perteneceLista(pila->tope->dato,$1) != NULL){
											    mostrarError(3,num_lineas);	
												exit(1);	
											  }else{
												 if (((typeSpec == entero) && ($3->tipo == INT_TIPO)) || ((typeSpec == real) && ($3->tipo == FLOAT_TIPO))){
													/*HACER LA ASIGNACION. Deberia modificar mi tabla de simbolos ???*/			
													if (typeSpec == entero){
														insertar(pila->tope->dato, $1, "entero");
													}else{
														insertar(pila->tope->dato, $1, "real");
													}	
												 }else if ((typeSpec == real) && ($3->tipo == INT_TIPO)) {
													/*HACER LA ASIGNACION */															
													mostrarError(5,num_lineas);
													if (typeSpec == entero){
														insertar(pila->tope->dato, $1, "entero");
													}else{
														insertar(pila->tope->dato, $1, "real");
													}			
												 }else if ((typeSpec == entero) && ($3->tipo == FLOAT_TIPO)){
													mostrarError(1,num_lineas); /*asignacion invalida, decir esperaba un <> en la asignacion*/	
													exit(1);
												}
											  }
										     }

     | IDENTIFIER                            { if (perteneceLista(pila->tope->dato,$1) == NULL){
												 if (typeSpec == entero){
													 insertar(pila->tope->dato,$1,"entero"); 												
													 //mostrarLista(pila->tope->dato);	
												 }else{
													insertar(pila->tope->dato,$1,"real"); 
												    //mostrarLista(pila->tope->dato);
												 }
											   }else{
												  mostrarError(3,num_lineas);
												  exit(1);	
											   }	 			
											 }
     ;

TypeSpecifier : INT 	{ typeSpec = entero; }
	          | FLOAT 	{ typeSpec = real; }
	          ;

Statement : Compound 
          | Conditional 		   
          | Iteration 			   

          | BREAK ';' 			   { if (contarCiclo==0) {
								 	   mostrarError(12,num_lineas);
                              		   exit(1);
   						             }
						  		   }
		  | CONTINUE ';'  		   { if (contarCiclo==0) {
									   mostrarError(12,num_lineas);
                             		   exit(1);
   						             }
						 		   } 
		  |	RETURN Expression ';'  { if (($2->tipo == INT_TIPO) && (tipoRetorno == real)) {
										mostrarError(9,num_lineas);
										exit(1);
									 }else if (($2->tipo == FLOAT_TIPO) && (tipoRetorno == entero)) {
                                        mostrarError(4,num_lineas);
										exit(1);
 									 }
								   }		
		  | Computation ';' 
          | ';' 
          ;

Conditional : IF '(' Expression ')' Statement 				  { if ($3->tipo != INT_TIPO) {
																  mostrarError(8,num_lineas);	
																  exit(1); 
															    }															    
												  			  }		

			| IF '(' Expression ')' Statement ELSE Statement   { if ($3->tipo != INT_TIPO) {
																   mostrarError(8,num_lineas);	
																   exit(1); 
															     }																
														 	   }	 

;

Iteration : WHILE {contarCiclo = contarCiclo + 1;} '(' Expression  ')' Statement { contarCiclo = contarCiclo - 1;
												  								   if ($4->tipo != INT_TIPO){
																				     mostrarError(8,num_lineas);
																				     exit(1);
																			       }
																			     }
		  ;

Computation : Expression 		{ $$ = malloc (sizeof(struct tipoNum));
								  $$->tipo = $1->tipo; 
								}		
	    ;

Expression : IDENTIFIER '=' Expression 	   { Tupla* aux = perteneceTable(pila,$1);
											 if ( aux == NULL){
												mostrarError(2,num_lineas);						
												exit(1);						
											 }else{
											 	if (aux->tipo == "entero") {
													typeSpec =	entero;
												}else{	
													typeSpec =	real;
											 	}
											    if (typeSpec == entero && $3->tipo == FLOAT_TIPO){	
												    mostrarError(1,num_lineas);
												    exit(1);
											    }else if ((typeSpec == real) && ($3->tipo == INT_TIPO)) {
									                 $$ = malloc (sizeof(struct tipoNum));	
										        	 $$->tipo = FLOAT_TIPO;
													 mostrarError(5,num_lineas);
											 	}else if ((typeSpec == entero && $3->tipo == INT_TIPO) || (typeSpec == real && $3->tipo == FLOAT_TIPO)){
											    	 $$ = malloc (sizeof(struct tipoNum));	
													 $$->tipo = $3->tipo;
											 	}
											  }												    										  
											}
										   												
	   | Condition 					       { $$ = malloc(sizeof(struct tipoNum));
											 $$->tipo = $1->tipo; 
										   }
	   ;

Condition : Disjunction								   { $$ = malloc  (sizeof(struct tipoNum));
														 $$->tipo = $1->tipo; 
												   	   }			
   	      | Disjunction '?' Expression ':' Condition    { if ($1->tipo == INT_TIPO){
														     if ($3->tipo == $5->tipo){
															    $$->tipo = $3->tipo;		
															 }else{
																mostrarError(1,num_lineas);
																exit(1);
															 }																 
													      }else{	
																mostrarError(1,num_lineas);
																exit(1);
													      }	
													    }
	      ;

Disjunction : Conjunction					    	{ $$ = malloc (sizeof(struct tipoNum));
 												      $$->tipo = $1->tipo;  
												    }
	        | Disjunction BARRABARRA Conjunction	{ if ($1->tipo== INT_TIPO && $3->tipo == INT_TIPO) {
													      $$->tipo = $1->tipo;
												       }else{
													      mostrarError(1,num_lineas);
 														  exit(1);
        		                                       }	
												    }
	        ;

Conjunction : Comparison						 { $$ = malloc (sizeof(struct tipoNum));
 												   $$->tipo = $1->tipo; 
												 }
	        | Conjunction AMPERAMPER Comparison	 { if ($1->tipo== INT_TIPO && $3->tipo == INT_TIPO) {
													   $$->tipo = $1->tipo;
												   }else {
													   mostrarError(1,num_lineas);
													   exit(1);
        		                                   }	
												 }
	        ;	

Comparison : Relation				       { $$ = malloc (sizeof(struct tipoNum));
											 $$->tipo = $1->tipo;
										   }
	       | Relation IGUALIGUAL Relation  { if ($1->tipo== INT_TIPO && $3->tipo == INT_TIPO) {												
											    $$->tipo = $1->tipo;
										     }else{ 
											    mostrarError(1,num_lineas); 
												exit(1);
                                             }	
									       }
	       ;

Relation : Sum		     { $$ = malloc (sizeof(struct tipoNum));
					       $$->tipo = $1->tipo;					   
					     }

	     | Sum '<' Sum   { if ($1->tipo == INT_TIPO && $3->tipo == INT_TIPO){	
							 $$ = malloc (sizeof(struct tipoNum));						         
							 $$->tipo = INT_TIPO;
					       }else{
  						      mostrarError(1,num_lineas);
							  exit(1);
					      }						
					   }	

	    | Sum '>' Sum	 { if ($1->tipo == INT_TIPO && $3->tipo == INT_TIPO){	
						      $$ = malloc (sizeof(struct tipoNum));	
					          $$->tipo = INT_TIPO;
					       }else{ 
						      mostrarError(1,num_lineas);							
							  exit(1);
					       }						
					     }	
	   ;
 
Sum : Term			 { $$ = malloc (sizeof(struct tipoNum));				 
					   $$->tipo = $1->tipo; 
					 }
    | Sum '+' Term	 { $$ = malloc (sizeof(struct tipoNum));				
					   if ($1->tipo == INT_TIPO && $3->tipo == INT_TIPO){
					   		$$->tipo = INT_TIPO;								 
						    /*$$->numInt = ($1->numInt) + ($3->numInt);*/
					   }else if ($1->tipo==FLOAT_TIPO && $3->tipo==FLOAT_TIPO){
					        $$->tipo = FLOAT_TIPO;	
						    /*$$->numFloat = ($1->numFloat) + ($3->numFloat);*/
        			   }else if ($1->tipo==INT_TIPO && $3->tipo==FLOAT_TIPO) {
						    $$->tipo = FLOAT_TIPO;			
							mostrarError(5,num_lineas); /* warnning */									 
 							/*$$->numFloat = ($1->numInt) + ($3->numFloat);*/
				       }else if ($1->tipo==FLOAT_TIPO && $3->tipo==INT_TIPO) {
				            $$->tipo = FLOAT_TIPO;
							mostrarError(5,num_lineas); /* warnning */	
 							/*$$->numFloat = ($1->numFloat) + ($3->numInt);*/
				       }
					 }

    | Sum '-' Term	 { $$ = malloc (sizeof(struct tipoNum));				
					   if ($1->tipo == INT_TIPO && $3->tipo == INT_TIPO){
					   		$$->tipo = INT_TIPO;								 
						    /*$$->numInt = ($1->numInt) - ($3->numInt);*/
					   }else if ($1->tipo==FLOAT_TIPO && $3->tipo==FLOAT_TIPO){
					        $$->tipo = FLOAT_TIPO;	
						    /*$$->numFloat = ($1->numFloat) - ($3->numFloat);*/
        			   }else if ($1->tipo==INT_TIPO && $3->tipo==FLOAT_TIPO) {
						    $$->tipo = FLOAT_TIPO;			
							mostrarError(5,num_lineas); /* warnning */									 
 							/*$$->numFloat = ($1->numInt) - ($3->numFloat);*/
				       }else if ($1->tipo==FLOAT_TIPO && $3->tipo==INT_TIPO) {
				            $$->tipo = FLOAT_TIPO;
							mostrarError(5,num_lineas); /* warnning */	
 							/*$$->numFloat = ($1->numFloat) - ($3->numInt);*/
				       } 
 					 }
    ;

Term : Factor			   { $$ = malloc (sizeof(struct tipoNum));				 
							 $$->tipo = $1->tipo; 
						   } 
     | Term '*' Factor	   { $$ = malloc (sizeof(struct tipoNum));				
							 if ($1->tipo == INT_TIPO && $3->tipo == INT_TIPO){
					             $$->tipo = INT_TIPO;								 
								 /*$$->numInt = ($1->numInt) * ($3->numInt);*/
					  	     }else if ($1->tipo==FLOAT_TIPO && $3->tipo==FLOAT_TIPO){
					             $$->tipo = FLOAT_TIPO;	
								 /*$$->numFloat = ($1->numFloat) * ($3->numFloat);*/
            			     }else if ($1->tipo==INT_TIPO && $3->tipo==FLOAT_TIPO) {
						         $$->tipo = FLOAT_TIPO;			
								 mostrarError(5,num_lineas); /* warnning */									 
 								 /*$$->numFloat = ($1->numInt) * ($3->numFloat);*/
				             }else if ($1->tipo==FLOAT_TIPO && $3->tipo==INT_TIPO) {
   					             $$->tipo = FLOAT_TIPO;
								 mostrarError(5,num_lineas); 	/* warnning */	
 								 /*$$->numFloat = ($1->numFloat) * ($3->numInt);*/
				             }
						   }
     | Term '/' Factor	   { $$ = malloc (sizeof(struct tipoNum));				
							 if ($1->tipo == INT_TIPO && $3->tipo == INT_TIPO){
					             $$->tipo = INT_TIPO;								 
								 /*$$->numInt = ($1->numInt) / ($3->numInt);*/
					  	     }else if ($1->tipo==FLOAT_TIPO && $3->tipo==FLOAT_TIPO){
					             $$->tipo = FLOAT_TIPO;	
								 /*$$->numFloat = ($1->numFloat) / ($3->numFloat);*/
            			     }else if ($1->tipo==INT_TIPO && $3->tipo==FLOAT_TIPO) {
						         $$->tipo = FLOAT_TIPO;			
								 mostrarError(5,num_lineas); /* warnning */									 
 								 /*$$->numFloat = ($1->numInt) / ($3->numFloat);*/
				             }else if ($1->tipo==FLOAT_TIPO && $3->tipo==INT_TIPO) {
   					             $$->tipo = FLOAT_TIPO;
								 mostrarError(5,num_lineas); /* warnning */	
 								 /*$$->numFloat = ($1->numFloat) / ($3->numInt);*/
				             }
						   }
     | Term '%' Factor	   { if ($1->tipo == INT_TIPO && $3->tipo == INT_TIPO){
								 $$ = malloc (sizeof(struct tipoNum));				
					             $$->tipo = INT_TIPO;
								 /*$$->numInt = ($1->numInt)%($3->numInt);*/
					  	     }else{
								 mostrarError(7,num_lineas); /*modulo solo valido para enteros*/
							     exit(1);
							 } 
						   }
     ;

Factor : Primary	    { $$ = malloc (sizeof(struct tipoNum));				
						  $$->tipo = $1->tipo; 
                        }
       | '!' Factor		{ if ($2->tipo == INT_TIPO) {
							  $$ = malloc (sizeof(struct tipoNum));				
						      $$->tipo = INT_TIPO;
							  /*$$->numInt = !($2->numInt);*/	
 						  }else if ($2->tipo == FLOAT_TIPO) {
 						      mostrarError(6,num_lineas); /*! solo para int*/
							  exit(1);
						  }
						}
       | '-' Factor		{ if ($2->tipo == INT_TIPO) {
							  $$ = malloc (sizeof(struct tipoNum));			
						      $$->tipo = INT_TIPO;			
							  /*$$->numInt = -($2->numInt)*/
 						  }else{
							  $$ = malloc (sizeof(struct tipoNum));				
 						      $$->tipo = FLOAT_TIPO;
							  /*$$->numFloat = -($2.numFloat)*/
						  }
						}
       ;


Primary : INTDENOTATION		{ $$ = malloc(sizeof(struct tipoNum));	/*deberia ser (tipoNum*)malloc(sizeof(tipoNum));*/
							  $$->tipo = INT_TIPO;
							  /*$$->numInt = $1; $1 tiene el valor entero de INTDENOTATION*/
							}

		| FLOATDENOTATION 	{ $$ = malloc(sizeof(struct tipoNum));
							  $$->tipo = FLOAT_TIPO; 
							  /*$$->numFloat = $1;*/
							} 

		| IDENTIFIER        { if (perteneceTable(pila,$1) == NULL){
								 mostrarError(2,num_lineas); 
							  	 exit(1);
								 /*YYABORT; para cortar la ejecucion de yacc*/									
							  }else{
								 $$ = malloc(sizeof(struct tipoNum));
						         Tupla* aux = perteneceTable(pila,$1); 																
								 if (aux->tipo == "entero") {
									$$->tipo = INT_TIPO; 																	
								 }else{
									$$->tipo = FLOAT_TIPO;
								 }					
							   }
							}   

		| '(' Expression ')'	{ $$ = malloc(sizeof(struct tipoNum));
					  $$->tipo = $2->tipo; /*$$->nombre = $2->nombre;*/}
		;	
%%
typedef char * string;

/* Se invoca automaticamente al encontrar el final del archivo de entrada */
yywrap() 
{
	return 1;
}


/* Este metodo es llamado por yyparse si ocurre un error, o no se puede aplicar una regla */
/* En caso de ocurra dicho error se ejecutara el cuerpo de este metodo*/
yyerror (char *a) 
{
  printf ("%s\n", a);
  printf ("%s", "Verifique su sintaxis. **** ERROR EN LINEA: ");
  printf("%d **** \n",num_lineas ); 

}

/* Funcion que verifica si el nombre de un archivo es correcto. Es decir, solo tratamos archivos de extension .c-*/
int archivoValido(char* arch)  
{
   char*aux = arch;
   int f;
   int lenght =0;
   for (f=0; *arch != '\0'; f++) {
       lenght++;
      *arch++;
   }  
   int i = 0;	
   char c = aux[i];
   if (c != '.'){
      while (c != '.') {
         i++;
         c = aux[i]; 	
      }
      i++;
      c = aux[i];
      if (c == 'c') {
         i++;
         c = aux[i];
	 if (c == '-') {
	    i++;
	    c = aux[i];

	    if (i == lenght) {
		return 1;
   	    }else{
	       return 0;
	    }  
	 }else{
	    return 0;
	 }
      }else{
         return 0;
      }			     
   }else{
     return 0;
   }	 
}	

/* Si el archivo esta mal escrito, se posibilita volver a escribirlo, en caso de que no exista, se termina*/
int main(int argc, string argv[])
{
  string archivo;
  if (argc != 2) {
	printf("Mal uso. Uso corrector <./run.su archivo.c-> \n");
	exit(1);
  }  
  archivo = argv[1];
  if (archivoValido(archivo) == 0) {
  	printf("Archivo no  reconocido. Verifique su extension. \n");   
	exit(1);
  }else{
	yyin = fopen(archivo,"r");
  	if (yyin == NULL) {
        	printf("Archivo no existente. \n");
		exit(1);
 	}else{
		int c = yyparse(); 	  /* yyparse devuelve 0 en caso de finalizar el "parseo" con éxito */	  
		if(c == 0) {
			printf("COMPILACION EXITOSA\n");
			printf("Numero de lineas compiladas: ");
		        printf("%d\n",num_lineas );
		        printf("\n");
		}
		fclose(yyin);
		return 0;

	}
  } 
}
